--[[
	Copyright (C) Udorn (Blackhand)
--]]

local viewType = "ScrollView"
local version = 1
local YakaGui = LibStub("YakaGui", true)
local info = YakaGui:GetViewInfo(viewType) 
if (info and info.version >= version) then 
	return 
end

local log = vendor.Debug:new("ScrollView")

yaka = yaka or {}
yaka.ScrollView = {}
yaka.ScrollView.prototype = {}
yaka.ScrollView.metatable = {__index = yaka.ScrollView.prototype}
setmetatable(yaka.ScrollView.prototype, {__index = yaka.View.prototype})

yaka.ScrollView.info = {viewType = viewType, version = version}
yaka.ScrollView.GetViewInfo = function()
	return yaka.ScrollView.info
end

local EPSILON = 0.001

local function _GetScrollChildHeight(self, width)
	if (not width) then
		if (self.scrollBarShown) then
			width = self.frame:GetWidth() - 18
		else
			width = self.frame:GetWidth()
		end
	end
	
	local h = self.scrollChild:GetMeasuredHeight(width)
	if (h == "fill") then
		error("can't handle free height container")
	end
	self.scrollChildHeight = h + 4 -- some extra padding
	return self.scrollChildHeight
end

local function _GetMaxValue(self, scrollChildHeight)
	return scrollChildHeight - self.frame:GetHeight()
end

local function _UpdateMaxValue(self, scrollChildHeight)
	local maxValue = _GetMaxValue(self, scrollChildHeight)
	self.scrollBar:SetMinMaxValues(0, math.max(0, maxValue))	
end

local function _UpdateScrollButtons(self, value)
 	if (value <= EPSILON) then
 		self.scrollDownButton:Enable()
 		self.scrollUpButton:Disable()
 	else
 		local scrollChildHeight = self.scrollChildHeight
 		if (not scrollChildHeight) then
 			scrollChildHeight = _GetScrollChildHeight(self)
 		end
		local maxValue = _GetMaxValue(self, scrollChildHeight)
		
		if (value >= maxValue - EPSILON) then
 			self.scrollDownButton:Disable()
 			self.scrollUpButton:Enable()
		else
 			self.scrollDownButton:Enable()
 			self.scrollUpButton:Enable()
		end
	end
end

local function _OnValueChanged(scrollBar, value)
	local self = scrollBar.obj
 	self.frame:SetVerticalScroll(value)
 	_UpdateScrollButtons(self, value)
end

local function _OnMouseWheel(frame, delta)
	local self = frame.view
	
	if (self.scrollBarShown) then
		local height = self.frame:GetHeight()
 		local scrollChildHeight = self.scrollChildHeight
 		if (not scrollChildHeight) then
 			scrollChildHeight = _GetScrollChildHeight(self)
 		end
		local maxValue = _GetMaxValue(self, scrollChildHeight)
		
	  	local value = self.scrollBar:GetValue()
	  	local newValue
      	if (IsShiftKeyDown() and (delta > 0)) then
        	newValue = 0
      	elseif (IsShiftKeyDown() and (delta < 0)) then
         	newValue = maxValue
      	elseif ((delta < 0) and (value < maxValue - EPSILON)) then
        	newValue = value + 10
      	elseif ((delta > 0) and (value > EPSILON)) then
         	newValue = value - 10
      	end
      	if (newValue) then
      		local value = math.min(maxValue, math.max(0, newValue))
      		self.scrollBar:SetValue(value)
      		_OnValueChanged(self.scrollBar, value)
      	end
	end
end

local function _InitFrame(self)
	-- create scroll frame
	local frame = CreateFrame("ScrollFrame", nil, frame)
	frame.view = self
	frame:SetPoint("TOPLEFT")
	frame:SetPoint("BOTTOMRIGHT")
	frame:SetScript("OnMouseWheel", _OnMouseWheel)
	frame:EnableMouseWheel(true)

	-- create scroll bar
	local name = "YakaScrollViewSB"..YakaGui:CreateId()
	local scrollBar = CreateFrame("Slider", name, frame, "UIPanelScrollBarTemplate")
	scrollBar.obj = self
	scrollBar:SetOrientation("VERTICAL") 
	scrollBar:SetPoint("TOPLEFT", frame, "TOPRIGHT", -18, -16)
	scrollBar:SetPoint("BOTTOMLEFT", frame, "BOTTOMRIGHT", -18, 16)
	scrollBar:SetValueStep(self.cfg.valueStep)
	scrollBar:SetValue(0)
	scrollBar:SetWidth(16)
	scrollBar:Hide()
	scrollBar:SetScript("OnValueChanged", _OnValueChanged)
	scrollBar:Hide()
	
	-- remember buttons
	local scrollUpButton = _G[name.."ScrollUpButton"]
	local scrollDownButton = _G[name.."ScrollDownButton"]
	
	-- background color for the scroller
	local scrollBarBackground = scrollBar:CreateTexture(nil, "BACKGROUND")
	scrollBarBackground:SetAllPoints(scrollBar)
	scrollBarBackground:SetTexture(unpack(self.cfg.scrollBarBackgroundColor))
	
	-- container frame
	local scrollChild = YakaGui:CreateView(self.scrollChildIn)
	frame:SetScrollChild(scrollChild.frame)
	
	self.frame = frame
	self.scrollBar = scrollBar
	self.scrollChild = scrollChild
	self.scrollUpButton = scrollUpButton
	self.scrollDownButton = scrollDownButton
end

local function _Layout(self, x, y, width, height, forceBar)
	--log:Debug("Layout x [%s] y [%s] width [%s] height [%s]", x, y, width, height)
	
	local scrollChildHeight = _GetScrollChildHeight(self, width)
	
	if ((scrollChildHeight <= height) and self.scrollBarShown) then
		self.scrollBarShown = nil
		self.scrollBar:Hide()
	elseif (forceBar or ((scrollChildHeight > height) and not self.scrollBarShown)) then
		self.scrollBarShown = true
		self.scrollBar:Show()
		self.scrollBar:SetValue(0)
		-- get new height for "fit" text views
	end

	if (self.scrollBarShown) then
		self.scrollChild:Layout(0, 0, width - 18, nil)
	else
		self.scrollChild:Layout(0, 0, width, nil)
	end
	
	-- recheck child height
	self.scrollChildHeight = self.scrollChild:GetHeight() + 4
	if (not self.scrollBarShown and self.scrollChildHeight > height) then
		-- upps, try again with scroll bar
		_Layout(self, x, y, width, height, true)
	else
    	yaka.View.prototype.Layout(self, x, y, width, height)
    	
    	_UpdateMaxValue(self, self.scrollChildHeight)
     	_UpdateScrollButtons(self, self.scrollBar:GetValue())
     end
end

--[[ 
	Creates a new instance.
--]]
function yaka.ScrollView:new(cfgIn)
	local instance = setmetatable({}, self.metatable)
	instance:InitCfg(cfgIn)
	instance:InitFrame()
	return instance
end

function yaka.ScrollView.prototype:InitCfg(cfgIn)
	yaka.View.prototype.InitCfg(self, cfgIn)
	
	local cfg = self.cfg
	cfg.valueStep = cfgIn.valueStep or 1
	
	if (not cfgIn.scrollChild) then
		error("no scrollChild defined for ScrollView")
	end
	self.scrollChildIn = cfgIn.scrollChild
	cfg.scrollBarBackgroundColor = cfgIn.scrollBarBackgroundColor or {0.2, 0.2, 0.2, 0.8}
end

function yaka.ScrollView.prototype:InitFrame()
	_InitFrame(self)
	yaka.View.prototype.InitFrame(self)
end

function yaka.ScrollView.prototype:Layout(x, y, width, height, forceBar)
	--log:Debug("Layout x [%s] y [%s] width [%s] height [%s]", x, y, width, height)
	if (self.isLayouting) then
		return
	end
	self.isLayouting = true
	
	 _Layout(self, x, y, width, height)
 	
	self.isLayouting = nil
end

-- register view

YakaGui:RegisterViewType(yaka.ScrollView)